package com.lp.chapter001.handler.easyexcel;

import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.IdUtil;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.read.listener.ReadListener;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.lp.chapter001.entity.Student;
import com.lp.chapter001.excel.easyexcel.EasyExcelImportExcel;
import com.lp.chapter001.mapper.StudentMapper;
import io.micrometer.common.util.StringUtils;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;

/**
 * easyExcel 导入
 * @author LP to 2024/04/02
 */
@Slf4j
@Service
@Scope("prototype")
public class EasyExcelStudentImportHandler implements ReadListener<EasyExcelImportExcel> {

    /** 代校验数据集合 */
    private final CopyOnWriteArrayList<EasyExcelImportExcel> checkList = new CopyOnWriteArrayList<>();
    /** 成功数据集合 */
    private final CopyOnWriteArrayList<EasyExcelImportExcel> successList = new CopyOnWriteArrayList<>();
    /** 失败数据集合 */
    private final CopyOnWriteArrayList<EasyExcelImportExcel> failList = new CopyOnWriteArrayList<>();
    /** 批处理条数 */
    private final static int BATCH_COUNT = 20000;
    private Set<String> studentNumSet = new HashSet<>();
    @Resource
    private ThreadPoolExecutor easyExcelStudentImportThreadPool;
    @Resource
    private StudentMapper studentMapper;

    /**
     * 读取表格内容，每一条数据解析都会来调用
     * @author LP to 2024/4/7
     */
    @Override
    public void invoke(EasyExcelImportExcel importExcel, AnalysisContext analysisContext) {
        // 参数校验
        if (StringUtils.isBlank(importExcel.getName())) {
            importExcel.setErrorMsg("学生名称不能为空");
            failList.add(importExcel);
            return;
        }
        if (StringUtils.isBlank(importExcel.getStudentNum())) {
            importExcel.setErrorMsg("学号不能为空");
            failList.add(importExcel);
            return;
        }
        // 这里用学号作为唯一值,用于判断文件里的数据是否有重复
        if (studentNumSet.contains(importExcel.getStudentNum())) {
            importExcel.setErrorMsg("学号重复(文件)");
            failList.add(importExcel);
            return;
        }
        studentNumSet.add(importExcel.getStudentNum());

        // 版本①：每条数据进行数据库查询
        // if (studentMapper.selectCount(Wrappers.<Student>lambdaQuery().eq(Student::getStudentNum, importExcel.getStudentNum())) > 0) {
        //     importExcel.setErrorMsg("学号重复(数据库)");
        //     failList.add(importExcel);
        //     return;
        // }
        checkList.add(importExcel);
        if (checkList.size() >= BATCH_COUNT) {
            checkData();
            saveDate();
        }
    }

    /**
     * 在转换异常 获取其他异常下会调用本接口。抛出异常则停止读取。如果这里不抛出异常则 继续读取下一行。
     * @author LP to 2024/4/7
     */
    @Override
    public void onException(Exception exception, AnalysisContext context) throws Exception {
        // 导入异常，实际业务可以做一些异常处理。比如记录失败数据等
    }

    /**
     * 所有数据解析完成了调用
     * @author LP to 2024/4/7
     */
    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
        saveDate();
    }

    /**
     * 版本②：校验数据
     * @author Liupeng to 2024/9/30
     */
    private void checkData() {
        // 拆分list，每个list 2000 条数据
        List<List<EasyExcelImportExcel>> lists = ListUtil.split(checkList, 2000);
        final CountDownLatch countDownLatch = new CountDownLatch(lists.size());
        for (List<EasyExcelImportExcel> list : lists) {
            easyExcelStudentImportThreadPool.execute(() -> {
                try {
                    // 判断学号是否重复，批量查询学号
                    Map<String, EasyExcelImportExcel> studentNumMap = list.stream().collect(Collectors.toMap(EasyExcelImportExcel::getStudentNum, o -> o, (t1, t2) -> t2));
                    List<Student> studentList = studentMapper.selectList(Wrappers.<Student>lambdaQuery().in(Student::getStudentNum, studentNumMap.keySet()));
                    if (null == studentList) {
                        // 为空，表示没有重复
                        successList.addAll(list);
                    } else {
                        // 不为空，则需要遍历比较哪些值重复，将重复值从map删除，剩下map里的值就是不重复的
                        studentList.forEach(o -> {
                            EasyExcelImportExcel importExcel = studentNumMap.get(o.getStudentNum());
                            importExcel.setErrorMsg("学号重复(数据库)");
                            failList.add(importExcel);
                            studentNumMap.remove(o.getStudentNum());
                        });
                        successList.addAll(studentNumMap.values().stream().toList());
                    }
                } catch (Exception e) {
                    log.error("数据校验失败,e:{}", e.getMessage(), e);
                } finally {
                    countDownLatch.countDown();
                }
            });
        }
        // 等待所有线程执行完
        try {
            countDownLatch.await();
        } catch (Exception e) {
            log.error("等待所有线程执行完异常,e:{}", e.getMessage(), e);
        }
        // 提前将不再使用的集合清空，释放资源
        checkList.clear();
        lists.clear();
    }

    /**
     * 保存信息
     * @author Liupeng to 2024/4/2
     */
    private void saveDate() {
        // 拆分list，每个list 2000 条数据
        List<List<EasyExcelImportExcel>> lists = ListUtil.split(successList, 2000);
        final CountDownLatch countDownLatch = new CountDownLatch(lists.size());
        for (List<EasyExcelImportExcel> list : lists) {
            easyExcelStudentImportThreadPool.execute(() -> {
                try {
                    studentMapper.insertBatch(list.stream().map(o -> {
                        Student student = new Student();
                        student.setNo(IdUtil.getSnowflakeNextId());
                        student.setName(o.getName());
                        student.setStudentNum(o.getStudentNum());
                        student.setAge(o.getAge());
                        student.setSex(o.getSex());
                        student.setBirthday(o.getBirthday());
                        return student;
                    }).collect(Collectors.toList()));
                } catch (Exception e) {
                    log.error("启动线程失败,e:{}", e.getMessage(), e);
                } finally {
                    countDownLatch.countDown();
                }
            });
        }
        // 等待所有线程执行完
        try {
            countDownLatch.await();
        } catch (Exception e) {
            log.error("等待所有线程执行完异常,e:{}", e.getMessage(), e);
        }
        // 提前将不再使用的集合清空，释放资源
        successList.clear();
        lists.clear();
    }
}
